-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

-- Description: 模拟DPU卡MCU芯片
local lu = require 'luaunit'
local utils = require 'mc.utils'
local bs = require 'mc.bitstring'
local crc8 = require 'mc.crc8'
local chip = {}

local WRITE_CMD = 0x20
local READ_CMD = 0x21
local ADDR = 0xD4

local request_data_bs <const> = bs.new([[<<
    lun,
    arg,
    opcode:16,
    offset:32,
    length:32,
    data/string
>>]])

local frame_template <const> = [[<<
    count:8,
    body:1/body
>>]]

local response_data_bs <const> = bs.new([[<<
    error_code:16,
    opcode:16,
    total_length:32,
    length:32,
    data:length/string
>>]])

local request_bs = bs.new(frame_template, { body = request_data_bs })
local response_bs = bs.new(frame_template, { body = response_data_bs })

local function check_SetSysTime_req(req, req_count)
    -- 只有一帧
    if req_count == 1 then
        lu.assertEquals(req.body.data[1], 29)  -- 秒
        lu.assertEquals(req.body.data[2], 16)  -- 分钟
        lu.assertEquals(req.body.data[3], 23)  -- 时
        lu.assertEquals(req.body.data[4], 1)   -- 日
        lu.assertEquals(req.body.data[5], 8)   -- 月
        lu.assertEquals(req.body.data[6], 24)  -- 年
        lu.assertEquals(req.body.data[6], 4)   -- 一周第几天
    end
end

local function check_SetBiosLogLevel_req(req, req_count)
    if req_count == 1 then
        lu.assertEquals(req.body.data[1], 1) -- 日志类型
        lu.assertEquals(req.body.data[2], 1) -- 打印等级
    end
end

local function check_SetSolSwitch_req(req, req_count)
    if req_count == 1 then
        lu.assertEquals(req.body.data[1], 1) -- 串口管理选项1的Type
        lu.assertEquals(req.body.data[2], 1) -- 串口管理选项1的Value
        lu.assertEquals(req.body.data[3], 5) -- 串口管理选项2的Type
        lu.assertEquals(req.body.data[4], 7) -- 串口管理选项2的Value
    end
end

local function check_SetPxeOption_req(req, req_count)
    if req_count == 1 then
        lu.assertEquals(req.body.data[1], 4) -- 功能项1的Type
        lu.assertEquals(req.body.data[2], 5) -- 功能项1的Value
    end
end

local function check_SetSdiSlot_req(req, req_count)
    if req_count == 1 then
        lu.assertEquals(req.body.data[1], 1) -- PCIe卡所在的槽位号
    end
end

local function check_SetHostOsStatus_req(req, req_count)
    if req_count == 1 then
        lu.assertEquals(req.body.data[1], 0) -- PCIe卡所在的槽位号
    end
end

local function check_SetBootOption_req(req, req_count)
    if req_count == 1 then
        lu.assertEquals(req.body.data[1], 1) -- 启动参数1的Type
        lu.assertEquals(req.body.data[2], 1) -- 启动参数1的Value
        lu.assertEquals(req.body.data[3], 2) -- 启动参数2的Type
        lu.assertEquals(req.body.data[5], 2) -- 启动参数2的Value
    end
end

local function check_ResetSystem_req(req, req_count)
    if req_count == 1 then
    end
end

local function check_SetNMI_req(req, req_count)
    if req_count == 1 then
    end
end

local function check_SetSystemAC_req(req, req_count)
    if req_count == 1 then
        lu.assertEquals(req.body.data[1], 1)
    end
end

local function check_SetResetLinkage_req(req, req_count)
    if req_count == 1 then
        lu.assertEquals(req.body.data[1], 1)
    end
end

local function check_SetSdiIpv4_req(req, req_count)
    if req_count ==1 then
        lu.assertEquals(req.body.data[1], 0) -- 配置对象 0 - BMC， 1 - 卡上OS
        lu.assertEquals(req.body.data[2], 192) -- 2-5 IPv4地址
        lu.assertEquals(req.body.data[3], 168) 
        lu.assertEquals(req.body.data[4], 2)
        lu.assertEquals(req.body.data[5], 1)
        lu.assertEquals(req.body.data[6], 192) -- 6-9 Mask地址
        lu.assertEquals(req.body.data[7], 168)
        lu.assertEquals(req.body.data[8], 2)
        lu.assertEquals(req.body.data[9], 2)
        lu.assertEquals(req.body.data[10], 1) -- 10-11 Vlan号
        lu.assertEquals(req.body.data[11], 0)
    end
end

local function check_SetSdiIpv6_req(req, req_count)
    if req_count ==1 then
        lu.assertEquals(req.body.data[1], 0) -- 配置对象 0 - BMC， 1 - 卡上OS
        lu.assertEquals(req.body.data[2], 0) -- 2-17 IPv6地址
        lu.assertEquals(req.body.data[3], 0)
        lu.assertEquals(req.body.data[4], 0)
        lu.assertEquals(req.body.data[5], 0)
        lu.assertEquals(req.body.data[6], 0)
        lu.assertEquals(req.body.data[7], 0)
        lu.assertEquals(req.body.data[8], 0)
        lu.assertEquals(req.body.data[9], 0)
        lu.assertEquals(req.body.data[10], 0)
        lu.assertEquals(req.body.data[11], 0)
        lu.assertEquals(req.body.data[12], 0)
        lu.assertEquals(req.body.data[13], 0)
        lu.assertEquals(req.body.data[14], 0)
        lu.assertEquals(req.body.data[15], 0)
        lu.assertEquals(req.body.data[16], 0)
        lu.assertEquals(req.body.data[17], 1)
        lu.assertEquals(req.body.data[18], 1) -- 17 Mask地址
        lu.assertEquals(req.body.data[19], 1) -- 19-20 Vlan号
        lu.assertEquals(req.body.data[20], 0)
    end
end

local req_switch = {
    [0x0015] = {
        req_count = 0,
        check_func = function (req, req_count)
            if req_count < 4 then
                -- 模拟读写异常
                error('request failed')
            end
        end
    },
    [0x1000] = {
        req_count = 0,
        check_func = check_SetBootOption_req
    },
    [0x1005] = {
        res_by_data = true
    },
    [0x1006] = {
        req_count = 0,
        check_func = check_SetSolSwitch_req
    },
    [0x100E] = {
        req_count = 0,
        check_func = check_SetSdiSlot_req
    },
    [0x1010] = {
        [0x00] = {
            req_count = 0,
            check_func = check_SetSdiIpv4_req
        },
        [0xF0] = {
            req_count = 0,
            check_func = check_SetSdiIpv6_req
        }
    },
    [0x1017] = {
        req_count = 0,
        check_func = check_SetSysTime_req
    },
    [0x1018] = {
        req_count = 0,
        check_func = check_ResetSystem_req
    },
    [0x1019] = {
        req_count = 0,
        check_func = check_SetSystemAC_req
    },
    [0x101E] = {
        req_count = 0,
        check_func = check_SetBiosLogLevel_req
    },
    [0x101F] = {
        res_by_data = true
    },
    [0x1023] = {
        req_count = 0,
        check_func = check_SetResetLinkage_req
    },
    [0x1042] = {
        req_count = 0,
        check_func = check_SetNMI_req
    },
    [0x1054] = {
        req_count = 0,
        check_func = check_SetHostOsStatus_req
    },
    [0x1055] = {
        req_count = 0,
        check_func = check_SetPxeOption_req
    }
}

local res_switch = {
    [0x0000] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x0000,
                total_length = 5,
                length = 5,
                data = '\xee\xe1\x01\x00\x00'
            }
        }
    },
    [0x0001] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x0001,
                total_length = 1,
                length = 1,
                data = '\x00'
            }
        }
    },
    [0x0002] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x0002,
                total_length = 2,
                length = 2,
                data = '\x00\x00'
            }
        }
    },
    [0x0004] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x0004,
                total_length = 2,
                length = 2,
                data = '\x08\x07'
            }
        }
    },
    [0x0005] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x0005,
                total_length = 3,
                length = 3,
                data = '\x01\x02\x03'
            }
        }
    },
    [0x0011] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x0011,
                total_length = 2,
                length = 2,
                data = '\x10\x00'
            }
        }
    },
    [0x0015] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x0015,
                total_length = 0,
                length = 0,
                data = ''
            }
        },
        args = {
            [0x21] = 'Huawei',
            [0x22] = 'IT21SHSN',
            [0x23] = '2106310363FSQ6000554',
            [0x24] = '5104-00781'
        }
    },
    [0x001A] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1A,
                total_length = 1,
                length = 1,
                data = '\x01\x32'
            }
        }
    },
    [0x001D] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x001D,
                total_length = 51,
                length = 51,
                data = '\x05\x43\x50\x55\x00\x00\x00\x00\x00\x1a\x00' ..   --对应设备name  CPU
                            '\x53\x46\x50\x31\x00\x00\x00\x00\x20\x00' ..  --SFP1
                            '\x53\x46\x50\x32\x00\x00\x00\x00\x30\x00' ..  --SFP2
                            '\x49\x6e\x6c\x65\x74\x31\x00\x00\x32\x00' ..  --Inlet1
                            '\x4f\x75\x74\x6c\x65\x74\x31\x00\x2b\x00'     --Outlet1
            }
        }
    },
    [0x1000] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1000,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1001] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1001,
                total_length = 4,
                length = 4,
                data = '\x01\x01\x02\x02'
            }
        }
    },
    [0x1005] = {
        ['\x01'] = {
            rsp_count = 0,
            [1] = {
                count = 11,
                body = {
                    error_code = 0,
                    opcode = 0x1005,
                    total_length = 2,
                    length = 2,
                    data = '\x01\x01'
                }
            }
        },
        ['\x06'] = {
            rsp_count = 0,
            [1] = {
                count = 11,
                body = {
                    error_code = 0,
                    opcode = 0x1005,
                    total_length = 2,
                    length = 2,
                    data = '\x06\x03'
                }
            }
        }
    },
    [0x1006] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1006,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1007] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1007,
                total_length = 2,
                length = 2,
                data = '\x05\x07'
            }
        }
    },
    [0x100E] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x100E,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1010] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1010,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1011] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1011,
                total_length = 10,
                length = 10,
                data = '\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x01\x00'
            }
        }
    },
    [0x1017] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1017,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1018] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1018,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1019] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1019,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x101B] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x101E,
                total_length = 9,
                length = 9,
                data = '\x01\x00\x08\x00\x11\x22\x33\x44\x55'
            }
        }
    },
    [0x101E] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x101E,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x101F] = {
        ['\x01'] = {
            rsp_count = 0,
            [1] = {
                count = 11,
                body = {
                    error_code = 0,
                    opcode = 0x101F,
                    total_length = 1,
                    length = 1,
                    data = '\x01'
                }
            }
        },
        ['\x02'] = {
            rsp_count = 0,
            [1] = {
                count = 11,
                body = {
                    error_code = 0,
                    opcode = 0x101F,
                    total_length = 1,
                    length = 1,
                    data = '\x02'
                }
            }
        }
    },
    [0x1020] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1020,
                total_length = 4,
                length = 4,
                data = '\x03\x01\x04\x01'
            }
        }
    },
    [0x1021] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1021,
                total_length = 8,
                length = 8,
                data = '\x00\x00\x00\x00\x00\x00\x61\x30'
            }
        }
    },
    [0x1023] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1023,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1024] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1024,
                total_length = 1,
                length = 1,
                data = '\x01'
            }
        }
    },
    [0x1032] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1032,
                total_length = 4,
                length = 4,
                data = '\x01\x02\x00\x01'
            }
        }
    },
    [0x1042] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1042,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1046] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1046,
                total_length = 1,
                length = 1,
                data = '\x01'
            }
        }
    },
    [0x1047] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1047,
                total_length = 2,
                length = 2,
                data = '\x01\x02'
            }
        }
    },
    [0x104A] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x104A,
                total_length = 1,
                length = 1,
                data = '\x02'
            }
        }
    },
    [0x1054] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1054,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1055] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1055,
                total_length = 0,
                length = 0,
                data = ''
            }
        }
    },
    [0x1056] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x1056,
                total_length = 2,
                length = 2,
                data = '\x04\x04'
            }
        }
    },
    [0x105D] = {
        rsp_count = 0,
        [1] = {
            count = 11,
            body = {
                error_code = 0,
                opcode = 0x105D,
                total_length = 3,
                length = 3,
                data = '\x01\x02\x03'
            }
        }
    }
}

local function response(req, res_by_data)
    local res
    local arg_data
    if res_switch[req.body.opcode].args and res_switch[req.body.opcode].args[req.body.arg] then
        arg_data = res_switch[req.body.opcode].args[req.body.arg]
    end
    if res_by_data then
        res = res_switch[req.body.opcode][req.body.data]
        if type(arg_data) == 'table' and arg_data[req.body.data] then
            arg_data = arg_data[req.body.data]
        end
    else
        res = res_switch[req.body.opcode]
    end
    res.rsp_count = res.rsp_count + 1
    local res_tb = utils.table_copy(res[1])
    if type(arg_data) == 'string' then
        res_tb.body.data = arg_data
        res_tb.body.total_length = string.len(arg_data)
        res_tb.body.length = string.len(arg_data)
    end
    local value = response_bs:pack(res_tb)
    local check_buf = table.concat({string.char(ADDR), string.char(READ_CMD), string.char(ADDR | 0x01), value})
    return value .. string.char(crc8(check_buf))
end

chip.ComboWriteRead = function(_, ctx, write_cmd, write_data, read_cmd, read_len)
    write_data = write_data:sub(1, #write_data - 1)
    lu.assertEquals(write_cmd, WRITE_CMD)
    lu.assertEquals(read_cmd, READ_CMD)

    local req = request_bs:unpack(write_data, true)

    local req_check = req_switch[req.body.opcode]
    if req_check then
        if req_check[req.body.arg] then
            req_check = req_check[req.body.arg]
        end

        if req_check.req_count then
            req_check.req_count = req_check.req_count + 1
            req_check.check_func(req, req_check.req_count)
        end
    end

    return response(req, req_check and req_check.res_by_data)
end

return chip